

#include <physm.h>
#include <pio.h>

/**
 * The simulate function is not re-entrant. this forces only one simulation
 * to occur at once. There is some hidden state associated with a simulation.
 */
pthread_mutex_t _physm_sim_lock;
physm_que_t _physm_sim_que;
long _physm_next_tid = 0;

void _init(){

  pthread_mutex_init(&_physm_sim_lock, NULL);

}

int physm_world_init(physm_world_t *world, char *env, double tau){

  memset(world, 0, sizeof(physm_world_t));

  int err;
  world->bodies = physm_load_data(env, 1, &err);
  if (err != 0){
    return 1;
  }
  
  world->tau = tau;
  pthread_mutex_init(&world->lock, NULL);

  return 0;

}

int physm_simulate(physm_world_t *world, long time_steps, int threads){

  if (pthread_mutex_trylock(&_physm_sim_lock) != 0){
    /* If we dont return we run the risk of deadlock */
    return 1;
  }

  fprintf(stderr, "[physm-simulate] Running simulation:\n");
  fprintf(stderr, 
	  "[physm-simulate]   Bodies=%d  Tau=%lf time-steps=%ld threads=%d\n",
	  physm_llist_len(world->bodies), world->tau, time_steps, threads);

  fprintf(stderr, "[physm-simulate]   World: ");
  /* Start setting up the world. */
  PHYSM_LOCK(world);
  world->step = 0;
  world->t = 0;
  physm_que_init(&_physm_sim_que, world->bodies);
  PHYSM_UNLOCK(world);
  fprintf(stderr, "OK\n");

  _physm_next_tid = 0;

  /* Now were ready to start. */
  _physm_do_sim(world, time_steps, threads);

  pthread_mutex_unlock(&_physm_sim_lock);
  fprintf(stderr, "[physm-simulate] Simulation done.\n");
  return 0;

}

int _physm_run_tstep(physm_world_t *world, int threads){

  PHYSM_LOCK(world);

  world->t = (double) world->step * world->tau;
  _physm_thread_run_calc_force(world, threads);
  _physm_thread_run_reloc_bodies(world, threads);

  PHYSM_UNLOCK(world);

  return 0;

}

int _physm_do_sim(physm_world_t *world, long t_steps, int threads){

  long current_step = 0;

  long tstep_print_loc = t_steps / 10;

  while (current_step++ < t_steps){
    _physm_run_tstep(world, threads);
    if (current_step % tstep_print_loc == 0){
      double frac_done = (double) current_step / (double) t_steps;
      fprintf(stderr, "[physm-simulate] %d%% done.\n", 
	      (int) (frac_done * (double) 100));
    }
  }

  return 0;

}
